/*
 * $Id: TcpSocket.cc,v 1.11 2006-02-08 11:36:16 bacon Exp $
 */

#include <xpp/net/TcpSocket.h>
#include <xp/bas/errno.h>

namespace xpp
{
	namespace net
	{
	
		TcpSocket::TcpSocket (): InetSocket (STREAM)
		{
		}

		int TcpSocket::open (Handle handle)
		{
			return InetSocket::open (handle);
		}

		int TcpSocket::open (Handle handle, const Address& target)
		{
			return InetSocket::open (handle, target);
		}

		int TcpSocket::open (const Address& address)
		{
			return InetSocket::open (address);
		}

		int TcpSocket::connect ()
		{
			XPP_NET_SOCKET_CHECK_CLOSED_HANDLE (this->sckhnd);
			return connect_socket (
				target.storage(), target.storageSize ());
		}

		int TcpSocket::connectx ()
		{
			XPP_NET_SOCKET_CHECK_CLOSED_HANDLE (this->sckhnd);
			return connect_socket (
				target.storage(), target.storageSize (), 
				(xp_bool_t(*)(void*))&TcpSocket::__cancel_connect);
		}

		int TcpSocket::listen (int backlog)
		{
			XPP_NET_SOCKET_CHECK_CLOSED_HANDLE (this->sckhnd);
			return listen_socket (backlog);
		}

		Socket* TcpSocket::accept (
			Socket* peer_sck, Address* peer_adr)
		{
			//XPP_NET_SOCKET_CHECK_CLOSED_HANDLE (this->sckhnd);
			if (this->sckhnd == INVALID_HANDLE) {
				this->error_code = ERR_CLOSED;
				return XP_NULL;
			}

			struct sockaddr_storage ss;
			addr_t* p = (addr_t*)&ss;
			len_t len = (len_t)xp_sizeof(ss);

			if (peer_sck == XP_NULL) {
				try { peer_sck = new TcpSocket (); }
				catch (...) { }

				if (peer_sck == XP_NULL) {
					drop ();
					this->error_code = ERR_MEMORY;
					return XP_NULL;
				}
			}

			if (xp_sck_acpt(this, peer_sck, p, &len) == XP_NULL) {
				this->error_code = translate_errno (
					XPP_NET_SOCKET_ERRNO, ERR_GENERAL);
				return XP_NULL;
			}

			if (peer_adr != XP_NULL) *(InetAddress*)peer_adr = ss;
			return peer_sck;
		}

		int TcpSocket::drop ()
		{
			xp_sck_t sck;	

			if (xp_sck_acpt (this, 
				&sck, XP_NULL, XP_NULL) != XP_NULL) {
				xp_sck_close (&sck);
				return 0;
			}

			return -1;
		}

		xp_bool_t TcpSocket::__cancel_connect (void* arg)
		{
			TcpSocket* tcp = (TcpSocket*)arg;	
			return (tcp->canCancelConnect())? xp_true: xp_false;
		}
	}
}
